package com.clei.utils.mybatis;

import com.clei.annotation.SensitiveEntity;
import com.clei.annotation.SensitiveField;
import com.clei.utils.CollectionUtil;
import com.clei.utils.PrintUtil;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 加密拦截器
 *
 * @author Y
 * @date 2023-09-17
 */
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)})
public class EncryptInterceptor implements Interceptor {

    private final DataEncryptor dataEncryptor = new AESDataEncryptor();

    /**
     * 拦截
     *
     * @param invocation invocation
     * @return Object
     * @throws Throwable Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            //@Signature 指定了 type= paramHandler 后，这里的 invocation.getTarget() 便是parameterHandler
            //若指定ResultSetHandler ，这里则能强转为ResultSetHandler
            ParameterHandler paramHandler = (ParameterHandler) invocation.getTarget();
            // 取出实例
            Object paramObj = paramHandler.getParameterObject();
            if (paramObj instanceof MapperMethod.ParamMap) {
                MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) paramObj;
                Set<Object> addSet = new HashSet<>(paramMap.size());
                for (Object value : paramMap.values()) {
                    if (!addSet.contains(value)) {
                        encrypt(value);
                        addSet.add(value);
                    }
                }
            } else {
                encrypt(paramObj);
            }
        } catch (Exception e) {
            PrintUtil.log("EncryptInterceptor intercept error ", e);
        }
        return invocation.proceed();
    }

    /**
     * 加密
     *
     * @param paramObj 参数对象
     */
    private void encrypt(Object paramObj) throws Exception {
        if (null == paramObj) {
            return;
        }
        boolean isList = paramObj instanceof List;
        Class<?> paramClass = null;
        if (isList) {
            List paramList = (List) paramObj;
            if (CollectionUtil.isEmpty(paramList)) {
                return;
            }
            paramClass = paramList.get(0).getClass();
        } else {
            paramClass = paramObj.getClass();
        }

        //校验该实例的类是否被@SensitiveEntity所注解
        boolean annotationPresent = null != paramClass && paramClass.isAnnotationPresent(SensitiveEntity.class);
        if (!annotationPresent) {
            return;
        }

        Field[] fields = paramClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(SensitiveField.class)) {
                if (isList) {
                    List paramList = (List) paramObj;
                    for (Object o : paramList) {
                        encrypt(o, field);
                    }
                } else {
                    encrypt(paramObj, field);
                }
            }
        }
    }

    /**
     * 加密
     *
     * @param obj Object
     * @param f   Field
     * @return encrypted value
     */
    private void encrypt(Object obj, Field f) throws Exception {
        Object value = f.get(obj);
        // instanceof value为null会返回false
        if (!(value instanceof String)) {
            return;
        }

        String encrypted = dataEncryptor.encrypt(String.valueOf(value));
        f.set(obj, encrypted);
    }
}
